package com.dyna.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dyna.constants.ModuleErrorCodeConstants;
import com.dyna.core.exception.CustomException;
import com.dyna.core.utils.CollUtil;
import com.dyna.core.utils.PageUtil;
import com.dyna.domain.req.PageParam;
import com.dyna.domain.resp.PageResult;
import com.dyna.enums.CommonStatusEnum;
import com.dyna.foundation.domain.entity.SysDept;
import com.dyna.foundation.domain.form.SysDeptSaveForm;
import com.dyna.foundation.domain.req.SysDeptListReq;
import com.dyna.foundation.domain.req.SysDeptPageReq;
import com.dyna.foundation.domain.resp.SysDeptPageResp;
import com.dyna.foundation.domain.resp.SysDeptSimpleResp;
import com.dyna.foundation.domain.resp.SysDeptTreeResp;
import com.dyna.mapper.SysDeptMapper;
import com.dyna.service.SysDeptService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.annotations.VisibleForTesting;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author zhoucaiwang
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements SysDeptService {

    @Override
    public Long createDept(SysDeptSaveForm createReqVO) {
        if (createReqVO.getParentId() == null) {
            createReqVO.setParentId(SysDept.PARENT_ID_ROOT);
        }
        // 校验父部门的有效性
        validateParentDept(null, createReqVO.getParentId());
        // 校验部门名的唯一性
        validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName());

        // 插入部门
        SysDept dept = BeanUtil.toBean(createReqVO, SysDept.class);
        baseMapper.insert(dept);
        return dept.getDeptId();
    }

    @VisibleForTesting
    void validateParentDept(Long id, Long parentId) {
        if (parentId == null || SysDept.PARENT_ID_ROOT.equals(parentId)) {
            return;
        }
        // 1. 不能设置自己为父部门
        if (Objects.equals(id, parentId)) {
            throw new CustomException(ModuleErrorCodeConstants.DEPT_PARENT_ERROR.getMsg());
        }
        // 2. 父部门不存在
        SysDept parentDept = baseMapper.selectById(parentId);
        if (parentDept == null) {
            throw new CustomException(ModuleErrorCodeConstants.DEPT_PARENT_NOT_EXITS.getMsg());
        }
        // 3. 递归校验父部门，如果父部门是自己的子部门，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentDept.getParentId();
            if (Objects.equals(id, parentId)) {
                throw new CustomException(ModuleErrorCodeConstants.DEPT_PARENT_IS_CHILD.getMsg());
            }
            // 3.2 继续递归下一级父部门
            if (parentId == null || SysDept.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            parentDept = baseMapper.selectById(parentId);
            if (parentDept == null) {
                break;
            }
        }
    }

    @VisibleForTesting
    void validateDeptNameUnique(Long id, Long parentId, String name) {
        SysDept dept = baseMapper.selectByParentIdAndName(parentId, name);
        if (dept == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的部门
        if (id == null) {
            throw new CustomException(ModuleErrorCodeConstants.DEPT_NAME_DUPLICATE.getMsg());
        }
        if (ObjectUtil.notEqual(dept.getDeptId(), id)) {
            throw new CustomException(ModuleErrorCodeConstants.DEPT_NAME_DUPLICATE.getMsg());
        }
    }

    @Override
    public void updateDept(SysDeptSaveForm updateReqVO) {
        if (updateReqVO.getParentId() == null) {
            updateReqVO.setParentId(SysDept.PARENT_ID_ROOT);
        }
        // 校验自己存在
        validateDeptExists(updateReqVO.getId());
        // 校验父部门的有效性
        validateParentDept(updateReqVO.getId(), updateReqVO.getParentId());
        // 校验部门名的唯一性
        validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());

        // 更新部门
        SysDept updateObj = BeanUtil.toBean(updateReqVO, SysDept.class);
        baseMapper.updateById(updateObj);
    }

    @VisibleForTesting
    void validateDeptExists(Long id) {
        if (id == null) {
            return;
        }
        SysDept dept = baseMapper.selectById(id);
        if (dept == null) {
            throw new CustomException(ModuleErrorCodeConstants.DEPT_NOT_FOUND.getMsg());
        }
    }

    @Override
    public void deleteDept(Long id) {
        // 校验是否存在
        validateDeptExists(id);
        // 校验是否有子部门
        if (baseMapper.selectCountByParentId(id) > 0) {
            throw new CustomException(ModuleErrorCodeConstants.DEPT_EXITS_CHILDREN.getMsg());
        }
        // 删除部门
        baseMapper.deleteById(id);
    }

    @Override
    public List<SysDeptPageResp> getSysDeptList() {
        List<SysDept> sysDeptList = baseMapper.selectList(new LambdaQueryWrapper<>());
        List<SysDeptPageResp> list = sysDeptList.stream().map(it -> BeanUtil.copyProperties(it, SysDeptPageResp.class)).collect(Collectors.toList());
        return list;
    }

    @Override
    public PageResult<SysDeptPageResp> pageList(SysDeptPageReq req) {
        PageParam pageParam = PageUtil.getPageParam();
        Page<SysDeptPageResp> page = PageHelper.startPage(pageParam.getPageNum(), pageParam.getPageSize()).doSelectPage(
                () -> baseMapper.selectPageList(req)
        );
        return new PageResult<>(page.getResult(), page.getTotal());
    }

    @Override
    public List<SysDeptTreeResp> getDeptTree() {
        List<SysDept> deptList = this.list(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getStatus, CommonStatusEnum.ENABLE.getStatus())
                .orderByAsc(SysDept::getOrderNum)
        );
        // 父部门ids
        Set<Long> parentIds = deptList.stream().map(SysDept::getParentId).collect(Collectors.toSet());
        // 部门ids
        Set<Long> deptIds = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toSet());
        // 根节点ids
        List<Long> rootIds = CollectionUtil.subtractToList(parentIds, deptIds);

        List<SysDeptTreeResp> list = new ArrayList<>();
        StopWatch watch = new StopWatch();
        watch.start();
        rootIds.forEach(rootId -> list.addAll(recurDeptTreeOptions(rootId, deptList)));
        watch.stop();
        log.info("部门列表查询耗时:::{}", watch.getTotalTimeMillis());
        return list;
    }

    /**
     * 递归生成部门表格层级列表
     *
     * @param parentId 父节点id
     * @param deptList 部门列表
     * @return List<SysDeptTreeResp>
     */
    public static List<SysDeptTreeResp> recurDeptTreeOptions(long parentId, List<SysDept> deptList) {
        List<SysDeptTreeResp> list = CollectionUtil.emptyIfNull(deptList).stream()
                .filter(dept -> dept.getParentId().equals(parentId))
                .map(dept -> {
                    SysDeptTreeResp deptTreeResp = BeanUtil.copyProperties(dept, SysDeptTreeResp.class);
                    List<SysDeptTreeResp> children = recurDeptTreeOptions(dept.getDeptId(), deptList);
                    if (CollectionUtil.isNotEmpty(children)) {
                        deptTreeResp.setChildren(children);
                    }
                    return deptTreeResp;
                })
                .collect(Collectors.toList());
        return list;
    }

    @Override
    public List<SysDept> getChildDeptList(Collection<Long> ids) {
        List<SysDept> children = new LinkedList<>();
        // 遍历每一层
        Collection<Long> parentIds = ids;
        for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下，存在死循环
            // 查询当前层，所有的子部门
            List<SysDept> depts = baseMapper.selectListByParentId(parentIds);
            // 1. 如果没有子部门，则结束遍历
            if (CollUtil.isEmpty(depts)) {
                break;
            }
            // 2. 如果有子部门，继续遍历
            children.addAll(depts);
            parentIds = CollUtil.convertSet(depts, SysDept::getDeptId);
        }
        return children;
    }

    @Override
    public List<SysDept> getDeptList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return baseMapper.selectListByIds(ids);
    }

    @Override
    public List<SysDept> getDeptList(SysDeptListReq reqVO) {
        List<SysDept> list = baseMapper.selectList(reqVO);
        list.sort(Comparator.comparing(SysDept::getCreateTime));
        return list;
    }

    @Override
    public List<SysDeptSimpleResp> getSimpleDeptList(SysDeptListReq reqVO) {
        List<SysDept> deptList = this.getDeptList(reqVO);
        List<SysDeptSimpleResp> result = deptList.stream()
                .map(dept -> new SysDeptSimpleResp(dept.getDeptId(), dept.getDeptName(), dept.getParentId()))
                .collect(Collectors.toList());
        return result;
    }

    @Override
    public Map<Long, SysDept> getDeptMap(Collection<Long> ids) {
        return SysDeptService.super.getDeptMap(ids);
    }

    @Override
    public List<SysDept> getChildDeptList(Long id) {
        return SysDeptService.super.getChildDeptList(id);
    }

    @Override
    public SysDept getDept(Long id) {
        return baseMapper.selectById(id);
    }

}




